package ia;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import gameplay.Game;
import gameplay.Grid;
import gameplay.PathGroup;
import gameplay.Point;



public class IA {

  Game game;
  Grid grid;
  private ArrayList<PathGroup> pathGroupList;
  private PathGroup thePathGroup;
  private static int garbagePoint;
  private static int garbagePointConstante = 0;
  
  public IA(Game game) {
    this.game = game;
    grid = game.getGrid();
    pathGroupList = this.grid.getPathGroupList();
    
    thePathGroup = buildThePathGroup(false);
    
  }
  
  
  public PathGroup buildThePathGroup(boolean risky) {
    thePathGroup = new PathGroup();
    PathGroup selectedPathGroup = new PathGroup();
    selectedPathGroup = selectedPathGroup();
    selectedPathGroup.sortPaths();
    garbagePoint = garbagePointConstante;
    
    for (int i = selectedPathGroup.getListOfPathSize() - 1; i >= 0; i--) {
      if (i == 0) {
        garbagePoint = Math.max(garbagePoint, 0);
        ArrayList<Point> firstPointList = selectedPathGroup.getListOfPath(0);
        addAPathTotThePathGroup(
            firstPointList.subList(garbagePoint, firstPointList.size()));
        addAPathTotThePathGroup(
            firstPointList.subList(0, garbagePoint));
        
      } else {
        addAPathTotThePathGroup(selectedPathGroup.getListOfPath(i));
        garbagePoint -= selectedPathGroup.getListOfPath(i).size();
      }

    }
    thePathGroup.sortPaths();
    return thePathGroup;
  }
  
  
  public void addAPathTotThePathGroup(List<Point> list) {
    thePathGroup.addToList(new ArrayList<Point>());
    for (int i = 0; i < list.size();i++) {
      if (i != 0 && i % Grid.scores.length == 0) {
        thePathGroup.addToList(new ArrayList<Point>());
      }
      thePathGroup.addToList(thePathGroup.getListOfPathSize() - 1,
          list.get(i));
    }
  }
  
  
  
  public PathGroup selectedPathGroup() {
    PathGroup selectedPathGroup = pathGroupList.get(0);
    int longestPath = 0;
    for (int i = 0; i < pathGroupList.size(); i++) {
      int currentPath = 0;
      PathGroup pathGroup = pathGroupList.get(i);
      for (int j = 0; j < pathGroup.getListOfPathSize(); j++) {
        currentPath = Math.max(currentPath, pathGroup.getListOfPath(j).size());
      }
      if (currentPath > longestPath) {
        longestPath = currentPath;
        selectedPathGroup = pathGroupList.get(i);
      } 
    }
    
    return selectedPathGroup;
  }
  
  
  public Point getNextPointGridOne(Game Game, int nextChip) {
    if (countPointFilled(thePathGroup) > thePathGroup.getNumberOfPoint() / 2) {
      buildThePathGroup(false);
    }
    
    
    int listOfPathSize = thePathGroup.getListOfPathSize();
    for (int i = 0; i < listOfPathSize; i++) {
      Point point = placeChipInThisListStatistically(nextChip, thePathGroup.getListOfPath(i));
      if (point != null && grid.getValueOfPointInGrid(point) == 0) {
        return point; 
      }
    }
    
    for (int i = listOfPathSize -1; i >= 0; i--) {
      Point point = placeChipInThisListFromEnd(thePathGroup.getListOfPath(i));
      if (point != null) {
        return point;
      }
    }
    return getNextPointIteratif(game, nextChip);
  }
  
  /**
   * 
   * @param chip
   * @param pointList
   * @return
   */
  private Point placeChipInThisListStatistically(int chip, ArrayList<Point> pointList) {

    int pointListSize = pointList.size();
    int inferiorIndex = 0;
    int superiorIndex = pointListSize - 1; 
    
    for (int i = 0; i < pointListSize; i++) {

      int pointValue = grid.getValueOfPointInGrid(pointList.get(i));
      if (pointValue != 0 && pointValue != 31) { 
        
        if (pointValue < chip) {
          inferiorIndex = Math.max(i, inferiorIndex);
        } else if (pointValue > chip) {
          superiorIndex = Math.min(i, superiorIndex);
        } else {
          if (i == 0) { 
            inferiorIndex = 0;
          } else if (grid.getValueOfPointInGrid(pointList.get(i - 1)) == 0) {
            superiorIndex = i;            
          } else { 
            inferiorIndex = i; 
          }
        } 
      }
      if (pointValue == 31) {
        if (inferiorIndex == i - 1) {
          inferiorIndex++;
        }
        if (i < pointListSize - 1) {
          int nextPointValue = grid.getValueOfPointInGrid(pointList.get(i + 1));
          if (nextPointValue != 0 && nextPointValue > chip) {
            superiorIndex = Math.min(i, superiorIndex);
          }
        }
      }
    }      
     
   if (chip == 31) {
     return getIndexForJoker(pointList); 
   }
   
   if (inferiorIndex == 0 && superiorIndex == 1 && grid.getValueOfPointInGrid(pointList.get(0)) == 0) {
     return pointList.get(0);
   }
   
   if (inferiorIndex + 1 == superiorIndex && superiorIndex == pointListSize - 1
       && grid.getValueOfPointInGrid(pointList.get(pointListSize - 1)) == 0) {
     return pointList.get(pointListSize - 1);
   }
   
    if (superiorIndex <= inferiorIndex + 1) {
      return null;
    } else {
      return pointList.get(
          getChipIndex(inferiorIndex, superiorIndex, chip, pointList));
    }
  }
  
  /**
   * Gets the point the more on the middle of the list for the joker.
   * @param pointList
   * @return
   */
  private Point getIndexForJoker(ArrayList<Point> pointList) {
    int pointListSize = pointList.size();
    if (pointListSize == 0)
      return null;
    for (int indexL = pointListSize / 2, indexR = pointListSize / 2; indexL >= 0; indexL --, indexR++ ) {
      if (grid.getValueOfPointInGrid(pointList.get(indexL)) == 0) {
        return pointList.get(indexL);
      }
      if (indexR < pointListSize && grid.getValueOfPointInGrid(pointList.get(indexR)) == 0) {
        return pointList.get(indexR);
      }
    }    
    
    return null;
  }
  
  /**
   * 
   * @param pointList
   * @return
   */
  private int countPointFilled(ArrayList<Point> pointList) {
    int pointFilled = 0;
    for (int i = 0; i < pointList.size(); i ++) {
      if (grid.getValueOfPointInGrid(pointList.get(i)) != 0) {
        pointFilled ++;
      }
    }
    return pointFilled;
  }
  
  
  private int countPointFilled(PathGroup pathGroup) {
    int pointFilled = 0;
    for (int i = 0; i < pathGroup.getListOfPathSize(); i ++) {
      pointFilled += countPointFilled(pathGroup.getListOfPath(i));
    }
    return pointFilled;
  }
  
  
  

  
  /**
   * 
   * @param inferiorIndex
   * @param superiorIndex
   * @param chip
   * @param pointList
   * @return
   */
  private int getChipIndex(int inferiorIndex, int superiorIndex, int chip,
      ArrayList<Point> pointList) {
    double rangePercent = getChipRangePercent(inferiorIndex, superiorIndex,
        chip, pointList);
    double indexDouble = inferiorIndex + (superiorIndex - inferiorIndex) 
        * rangePercent;
    int index = (int) Math.round(indexDouble);
    if (grid.getValueOfPointInGrid(pointList.get(index)) != 0
        && index == inferiorIndex) {
      index++;
    } else if (grid.getValueOfPointInGrid(pointList.get(index)) != 0
        && index == superiorIndex) {
      index--;
    }
    if (grid.getValueOfPointInGrid(pointList.get(index)) == 31) {
      if (index != 0 && 
          grid.getValueOfPointInGrid(pointList.get(index - 1)) == 0) {
        return index - 1;
      }
      if (index != pointList.size() - 1 &&
          grid.getValueOfPointInGrid(pointList.get(index + 1)) == 0) {
        return index - 1;
      }
    }
    
    return  index;
  }
  
  
  /**
   * 
   * @param inferiorIndex
   * @param superiorIndex
   * @param chip
   * @param pointList
   * @return
   */
      
  private double getChipRangePercent(int inferiorIndex, int superiorIndex, 
      int chip, ArrayList<Point> pointList) { 
    int chipMinValue = grid.getValueOfPointInGrid(pointList.get(inferiorIndex));
    if (chipMinValue == 31) {
        chipMinValue = (inferiorIndex != 0) ?
            grid.getValueOfPointInGrid(pointList.get(inferiorIndex - 1)) : 0;   
    }
    int chipMaxValue = grid.getValueOfPointInGrid(pointList.get(superiorIndex));
    if (chipMaxValue == 31) {
      chipMaxValue = (superiorIndex != pointList.size() - 1) ?
          grid.getValueOfPointInGrid(pointList.get(superiorIndex + 1)) : 
          game.getChips().get(game.getChips().size() - 1);   
    }
    if (chipMaxValue == 0) {
      chipMaxValue = game.getChips().get(game.getChips().size() - 1);
    }
    int chipAvailableBottom = 0;
    int chipAvailableUp = 0;
    int chipsSize = game.getChips().size();
    for (int i = 0; i < chipsSize;i++) {
      int curChip = game.getChips().get(i);
      chipAvailableBottom += (chipMinValue <= curChip && curChip <= chip) ? 1 : 0;
      chipAvailableUp += (chip <= curChip && curChip <= chipMaxValue) ? 1 : 0;
    } 
    if (chipAvailableUp + chipAvailableBottom == 0)
      return 0;
    return ((double)chipAvailableBottom) / (double)(chipAvailableUp + chipAvailableBottom);
  }
  
  
  /**
   * 
   * @param pointList
   * @return
   */
  public Point placeChipInThisListFromEnd(ArrayList<Point> pointList) {
    int pointListSize = pointList.size();
    if (pointListSize == 0) {
      return null;
    }
    for (int i = 0; i < pointListSize / 2 + 1; i++) {
      Point leftPoint = pointList.get(i);
      if (grid.getValueOfPointInGrid(leftPoint) == 0) {
        return leftPoint;
      }
      Point rightPoint = pointList.get(pointListSize - 1 - i);
      if (grid.getValueOfPointInGrid(rightPoint) == 0) {
        return rightPoint;
      }
    }
    return null;
  }
  
  
  
  
  
  
  
  
  /**
   * 
   * @param Game
   * @param nextChip
   * @return
   */
  public Point getNextPointIteratif(Game Game, int nextChip) {
    
    Iterator<PathGroup> pathGroupListIterator = pathGroupList.iterator();
    while (pathGroupListIterator.hasNext()) {
      PathGroup pathGroup = pathGroupListIterator.next();
       for (int i = 0; i < pathGroup.getListOfPathSize(); i++) {
         ArrayList<Point> pathList =  pathGroup.getListOfPath(i);
         Iterator<Point> pathListIterator = pathList.iterator();
         while (pathListIterator.hasNext()) {
           Point point = pathListIterator.next();
           if (grid.getValueOfPointInGrid(point) == 0) {
             return point;
           };
         }
       }
    }
    return null;
  }
  


  
}
